﻿#include "NeoBiCalibro.h"
#include "private/NeoBiCalibroPrivate.h"
#include <QMouseEvent>

NEO_PUBLIC_PROPERTY_CREATE_NOTIFY_CPP(NeoBiCalibro, int, Value)
NEO_PUBLIC_PROPERTY_CREATE_CPP(NeoBiCalibro, bool, ShowNotches)
NEO_PUBLIC_PROPERTY_CREATE_CPP(NeoBiCalibro, bool, ShowMarkers)
NEO_PUBLIC_PROPERTY_CREATE_CPP(NeoBiCalibro, QColor, PointerColor)
NEO_PUBLIC_PROPERTY_CREATE_CPP(NeoBiCalibro, QColor, BackgroundColor)
NEO_PUBLIC_PROPERTY_CREATE_CPP(NeoBiCalibro, QColor, NotchColor)
NEO_PUBLIC_PROPERTY_CREATE_CPP(NeoBiCalibro, QColor, MarkerColor)

NeoBiCalibro::NeoBiCalibro(QWidget *parent)
    : QWidget(parent), d_ptr(new NeoBiCalibroPrivate())
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  d->q_ptr = this;
}

NeoBiCalibro::~NeoBiCalibro() = default;

void NeoBiCalibro::setTickInterval(int interval)
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  if (interval < 1)
  {
    qWarning("[Neo] Tick interval cannot be less than 1 (%d).", interval);
  }
  else if (interval > d->pCurrentSpinAngle)
  {
    qWarning("[Neo] Tick interval %d cannot exceed current spin angle %d", interval, d->pCurrentSpinAngle);
  }
  else if (interval == d->pTickInterval)
  {
    qInfo("[Neo] Tick interval already at %d", interval);
  }
  else
  {
    const int old = d->pTickInterval;
    d->pTickInterval = interval;

    qInfo("[Neo] Tick interval updated %d -> %d", old, interval);
  }
}

int NeoBiCalibro::getTickInterval() const
{
  NEO_PUBLIC_POINT(const NeoBiCalibro);
  return d->pTickInterval;
}

void NeoBiCalibro::setMajorTickInterval(int interval)
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  if (interval < 1)
  {
    qWarning("[NeoBi] Major tick interval cannot be less than 1 (%d).", interval);
  }
  else if (interval > d->pCurrentSpinAngle)
  {
    qWarning("[NeoBi] Major tick interval %d cannot exceed current spin angle %d", interval, d->pCurrentSpinAngle);
  }
  else if (interval < d->pTickInterval)
  {
    qWarning("[NeoBi] Major tick interval %d cannot be less than tick interval %d",
             interval, d->pTickInterval);
  }
  else if (interval == d->pMajorTickInterval)
  {
    qInfo("[NeoBi] Major tick interval already at %d", interval);
  }
  else
  {
    const int old = d->pMajorTickInterval;
    d->pMajorTickInterval = interval;

    qInfo("[NeoBi] Major tick interval updated %d -> %d", old, interval);
  }
}

int NeoBiCalibro::getMajorTickInterval() const
{
  NEO_PUBLIC_POINT(const NeoBiCalibro);
  return d->pMajorTickInterval;
}

void NeoBiCalibro::setTextTickInterval(int interval)
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  if (interval < 1)
  {
    qWarning("[NeoBi] Text tick interval cannot be less than 1 (%d). ", interval);
  }
  else if (interval > d->pCurrentSpinAngle)
  {
    qWarning("[NeoBi] Text tick interval %d cannot exceed current spin angle %d", interval, d->pCurrentSpinAngle);
  }
  else if (interval < d->pTickInterval)
  {
    qWarning("[NeoBi] Text tick interval %d cannot be less than tick interval %d",
             interval, d->pTickInterval);
  }
  else if (interval < d->pMajorTickInterval)
  {
    qWarning("[NeoBi] Text tick interval %d cannot be less than major tick interval %d",
             interval, d->pMajorTickInterval);
  }
  else if (interval == d->pTextTickInterval)
  {
    qInfo("[NeoBi] Text tick interval already at %d", interval);
  }
  else
  {
    const int old = d->pTextTickInterval;
    d->pTextTickInterval = interval;

    qInfo("[NeoBi] Text tick interval updated %d -> %d", old, interval);
  }
}

int NeoBiCalibro::getTextTickInterval() const
{
  NEO_PUBLIC_POINT(const NeoBiCalibro);
  return d->pTextTickInterval;
}

void NeoBiCalibro::setMinAngle(int angle)
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  int spinAngle = d->pMaxAngle - angle;
  if (spinAngle < 0 || spinAngle > std::numeric_limits<int>::max())
  {
    qWarning("[NeoBi] Spin angle %lld out of int range: %lld", angle, spinAngle);
  }
  else if (spinAngle < d->pMinSpinAngle)
  {
    qWarning("[NeoBi] Spin angle %d is too small for min angle %d.",
             spinAngle, angle);
  }
  else if (spinAngle > d->pMaxSpinAngle)
  {
    qWarning("[NeoBi] Spin angle %d exceeds max spin angle %d.",
             spinAngle, d->pMaxSpinAngle);
  }
  else if (angle == d->pMinAngle)
  {
    qInfo("[NeoBi] Min angle already at %d", angle);
  }
  else
  {
    const quint64 old = d->pMinAngle;
    d->pMinAngle = angle;
    d->pCurrentSpinAngle = spinAngle;

    qInfo("[NeoBi] Min angle updated %d -> %d", old, angle);
  }
}

int NeoBiCalibro::getMinAngle() const
{
  NEO_PUBLIC_POINT(const NeoBiCalibro);
  return d->pMinAngle;
}

void NeoBiCalibro::setMaxAngle(int angle)
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  int spinAngle = angle - d->pMinAngle;
  if (spinAngle < 0 || spinAngle > std::numeric_limits<int>::max())
  {
    qWarning("[NeoBi] Spin angle %lld out of int range: %lld", angle, spinAngle);
  }
  else if (spinAngle < d->pMinSpinAngle)
  {
    qWarning("[NeoBi] Spin angle %d is too small for max angle %d.",
             spinAngle, angle);
  }
  else if (spinAngle > d->pMaxSpinAngle)
  {
    qWarning("[NeoBi] Spin angle %d exceeds max spin angle %d.",
             spinAngle, d->pMaxSpinAngle);
  }
  else if (angle <= d->pMinAngle)
  {
    qInfo("[NeoBi] Max angle %d must be greater than current min %d",
          angle, d->pMinAngle);
  }
  else
  {
    const quint64 old = d->pMaxAngle;
    d->pMaxAngle = angle;
    d->pCurrentSpinAngle = spinAngle;

    qInfo("[NeoBi] Max angle updated %d -> %d", old, angle);
  }
}

int NeoBiCalibro::getMaxAngle() const
{
  NEO_PUBLIC_POINT(const NeoBiCalibro);
  return d->pMaxAngle;
}

void NeoBiCalibro::setAngleRange(int min, int max)
{
  setMinAngle(min);
  setMaxAngle(max);
}

void NeoBiCalibro::mousePressEvent(QMouseEvent *event)
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  if (event->button() == Qt::LeftButton)
  {
    setValue(d->mousePositionForAngle(event->localPos()));
    this->update();
    event->accept();
    return;
  }
  return QWidget::mousePressEvent(event);
}

void NeoBiCalibro::mouseMoveEvent(QMouseEvent *event)
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  if (event->buttons() == Qt::LeftButton)
  {
    setValue(d->mousePositionForAngle(event->localPos()));
    this->update();
    event->accept();
    return;
  }
  return QWidget::mouseMoveEvent(event);
}

void NeoBiCalibro::resizeEvent(QResizeEvent *event)
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  d->updateWidgetGeometry(event->size());
  return QWidget::resizeEvent(event);
}

void NeoBiCalibro::paintEvent(QPaintEvent *event)
{
  NEO_PUBLIC_POINT(NeoBiCalibro)
  NEO_PAINTER_RESET
  d->drawBackground(&painter);
  d->drawNotches(&painter);
  d->drawPointer(&painter);
  d->drawMarks(&painter);
}
